﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using EF_Model;
using Moq;
using System;
using System.Data.Objects;

namespace EF_Tests
{
    [TestClass]
    public class RepositoryTest_Write
    {
        [TestMethod]
        public void Write_Test_With_Concrete_Context_And_Default_ObjectSet_Implementation()
        {
            DatabaseContext dbContext = new DatabaseContext();
            CustomerRepository cr = new CustomerRepository(dbContext);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            cr.AddCustomer(c);
            
            UnitOfWork unitOfWork = new UnitOfWork(dbContext);
            unitOfWork.Commit();

            Console.Write(cr.GetById("118").ContactName);//passes
        }

        [TestMethod]
        public void Write_Test_With_Concrete_Mock_Context_And_Default_ObjectSet_Implementation()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();
            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            cr.AddCustomer(c);
            
            UnitOfWork unitOfWork = new UnitOfWork(dbContext.Object);
            unitOfWork.Commit(); ;//passes, since dbContext.Customer has concrete implementation but 
            //1. item is not searchable
            //2. breaks read tests, as concrete dbContext.Customer is not testable 

            //Console.Write(cr.GetById("118").ContactName);
        }

        [TestMethod]
        public void Write_Test_With_Concrete_Mock_Context_And_Virtual_IObjectSet_Implementation()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();
            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            cr.AddCustomer(c);
            
            UnitOfWork unitOfWork = new UnitOfWork(dbContext.Object);
            unitOfWork.Commit();//fails, since dbContext.Customer has no instance due to virtual 
            //(the mock framework automatically creates an empty implementation for a virtual member)
            //and nevertheless GetById is not searchable

            //Console.Write(cr.GetById("118").ContactName);//fails, as not searchable
        }


        [TestMethod]
        public void Write_Test_With_Concrete_Mock_Context_And_Virtual_IObjectSet_Implementation_And_Mock_Setup_With_Direct_ObjectSet()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();

            //we can't create a mock instance of ObjectSet, as there is no default implementation for it
            dbContext.Setup(db => db.Customers).Returns(dbContext.Object.CreateObjectSet<Customer>());
                        
            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            cr.AddCustomer(c);//passes (actually do nothing internally)

            UnitOfWork unitOfWork = new UnitOfWork(dbContext.Object);
            unitOfWork.Commit();//passes (actually do nothing internally)
            
            
            //Console.Write(cr.GetById("118").ContactName);//fails, as not searchable
        }

        /*
         * RECOMMENED: since ... **
         * 1. (above method) dbContext.Object.CreateObjectSet points physically (although dosn't write actually) 
         * 2. (method below) since using Fake class don't really reflects the actual add/save commits scenario
         *  
        */
        [TestMethod]
        public void Write_Test_With_Concrete_Mock_Context_And_Virtual_IObjectSet_Implementation_And_Mock_Setup_With_Mock_ObjectSet()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();
            Mock<IObjectSet<Customer>> customers = new Mock<IObjectSet<Customer>>();
            //we can't create a mock instance of ObjectSet, as there is no default implementation for it
            
            dbContext.Setup(db => db.Customers).Returns(customers.Object);

            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            cr.AddCustomer(c);//passes (actually do nothing internally)
            
            UnitOfWork unitOfWork = new UnitOfWork(dbContext.Object);
            unitOfWork.Commit();//passes (actually do nothing internally)
            
            //Console.Write(cr.GetById("118").ContactName);//fails, as not searchable
        }

        [TestMethod]
        public void Write_Test_With_Concrete_Mock_Context_And_Virtual_IObjectSet_Implementation_And_Mock_Setup_With_Fake_Data()
        {
            Mock<DatabaseContext> dbContext = new Mock<DatabaseContext>();

            CustomerRepository cr = new CustomerRepository(dbContext.Object);

            Customer c = new Customer();
            c.CustomerID = "118";
            c.ContactName = "xxx-118";
            c.CompanyName = "com-001";

            dbContext.Setup(db => db.Customers).Returns(new FakeObjectSet<Customer>());

            cr.AddCustomer(c);//passes, 
            //lets the customer searchable, 
            //since the fake class add s it to the searchable collection (dbContext.Customers) instantly, 
            //without requiring commiting (calling SaveChanges())
            
            //if we want to reflect the actual scenario to have commit style write, we need to have another fake implementation of Context to handle this logic on in-memory objects
            
            //cr.SaveChanges();//passes
            
            Console.Write(cr.GetById("118").ContactName);//passes
        }
    }
}
